Deriving models with ``rise.microfound`` ======================================== Write the economics, not the algebra ------------------------------------ ``rise.microfound`` lets you author a DSGE model the way you would write it in a paper: declare the atoms and their roles, state each agent's optimization problem (period objective, choice variables, named constraints), and RISE **derives the first-order conditions symbolically** and generates a plain RISE model. It does not own or augment a model object -- its output is ordinary model code you can read, solve, and edit. Calibration and solving remain the responsibility of ``dsge_model``. The builder is a MATLAB handle object with a fluent (chained) interface. It is a model **definition** tool only: it never takes parameter *values* and has no ``solve`` method. A first example: the RBC model ------------------------------ .. code-block:: matlab p = rise.microfound("RBC"); p.endogenous("c","n","k","w","r","Y","A"); p.exogenous("eps"); p.parameter("beta","delta","psi","eta","alpha","rho"); p.agent("household", discount="beta") ... .maximize("log(c) - psi*n^(1+eta)/(1+eta)") ... .choose("c","n","k") ... .subject_to(budget="c + k = w*n + (1-delta+r)*k{-1}"); p.equilibrium( ... production="Y = A*k{-1}^alpha*n^(1-alpha)", ... labdemand ="w = (1-alpha)*A*k{-1}^alpha*n^(-alpha)", ... capdemand ="r = alpha*A*k{-1}^(alpha-1)*n^(1-alpha)", ... tfp ="A = A{-1}^rho*exp(eps)"); The object can be handed to ``dsge_model`` three equivalent ways: .. code-block:: matlab m = dsge_model(p); % (1) directly m = dsge_model(code(p)); % (2) via the generated cell array of code lines rs(p,'rbc.rs'); m = dsge_model('rbc.rs'); % (3) via a written .rs file Parameter **values** are passed to ``dsge_model`` at solve time, as a structure: .. code-block:: matlab params = struct('beta',0.99,'delta',0.025,'psi',1,'eta',1,'alpha',0.36,'rho',0.95); m = solve(m, 'parameters', params); % Number of solutions: 1 % steady state: c=2.555, n=0.928, k=35.24, w=2.371, r=0.0351, Y=3.436, A=1 What is generated ----------------- ``code(p)`` returns the derived model as a column cell array of code lines; ``rs(p,file)`` writes it to a ``.rs`` file, and ``show(p)`` prints it. For the RBC above the generated model is: .. code-block:: none @endogenous c n k w r Y A household_lambda_budget @exogenous eps @parameters beta delta psi eta alpha rho @model % production Y = A*k{-1}^alpha*n^(1-alpha); % labdemand w = (1-alpha)*A*k{-1}^alpha*n^(-alpha); % capdemand r = alpha*A*k{-1}^(alpha-1)*n^(1-alpha); % tfp A = A{-1}^rho*exp(eps); % household: budget (c + k)-(w*n + (1-delta+r)*k{-1}); % household: FOC wrt c (1)/(c)+household_lambda_budget*(1); % household: FOC wrt n -(((real_power(n,1+eta,1))*(psi))*(1+eta)/((1+eta)^(2)))+household_lambda_budget*(-(w)); % household: FOC wrt k household_lambda_budget*(1)+(beta)*household_lambda_budget{+1}*(-(1-delta+r{+1})); Three conventions to note: - **Lagrange multipliers** are added automatically as endogenous variables, named ``_lambda_`` (here ``household_lambda_budget``). The labour-supply and Euler equations are the derived FOCs; the budget constraint is emitted as a residual. - **Equation tags.** Every derived equation is preceded by a comment identifying its origin: ``% : `` for a constraint, ``% : FOC wrt `` for a first-order condition, and the label you gave for an equilibrium equation. These make the generated file self-documenting. - **No optimization block, nothing hidden.** The output is an ordinary RISE model: equilibrium identities, the derived FOCs, the agents' constraints, and the shock processes. There is no ``@optimization_problem`` block. .. note:: **Steady state.** ``microfound`` does *not* compute or require the analytical steady state -- providing it would mean hand-deriving exactly the algebra the tool automates. ``dsge_model`` forms the steady-state system from the derived FOCs and solves it **numerically**. You may pass ``p.steady_state(var=expr,...)`` with a rough *initial guess* (a single seed per variable) to help the solver; it is refined, not taken as the solution, and can often be omitted. This mirrors gEcon, which likewise derives and numerically solves the steady-state equations. Multiple agents --------------- An economy can have several agents; each ``agent(...)`` block is differentiated independently and contributes its own FOCs and multipliers. A firm that chooses capital and labour to maximise profit, for instance, is written as another agent: .. code-block:: matlab p.agent("firm") ... .maximize("A*k{-1}^alpha*n^(1-alpha) - w*n - r*k{-1}") ... .choose("k","n"); The gEcon example corpus (consumer + firm problems, adjustment costs, capital laws of motion, two-sector and two-country variants) is reproduced this way; see the validation note below. Regime switching ---------------- Regime switching enters through **switching parameters** -- and nothing else in how the agent's problem is written changes. Declare a Markov chain, mark the parameters that switch on it, and the derived FOCs become the correct, probability-weighted switching first-order conditions automatically. This is a capability gEcon does not have. .. code-block:: matlab p = rise.microfound("Switching-preferences RBC"); p.endogenous("c","n","k","w","r","Y","A"); p.exogenous("eps"); p.chain("pref", 2); % a 2-state Markov chain named "pref" p.parameter("beta","delta","eta","alpha","rho"); p.parameter("pref_tp_1_2","pref_tp_2_1"); % chain transition probabilities p.parameter("psi", switches_on="pref"); % labour disutility switches on "pref" p.agent("household", discount="beta") ... .maximize("log(c) - psi*n^(1+eta)/(1+eta)") ... .choose("c","n","k") ... .subject_to(budget="c + k = w*n + (1-delta+r)*k{-1}"); % ... equilibrium block unchanged ... The switching parameter is emitted under a chain-scoped header, ``@parameters(pref,2) psi``, and the labour FOC references ``psi`` (which now switches). Switching **values** and transition probabilities are supplied at solve time with the underscore convention ``__`` and ``_tp__``: .. code-block:: matlab params = struct('beta',0.99,'delta',0.025,'eta',1,'alpha',0.36,'rho',0.95, ... 'psi_pref_1',1, 'psi_pref_2',3, 'pref_tp_1_2',0.1, 'pref_tp_2_1',0.1); m = solve(dsge_model(p), 'parameters', params); % Number of solutions: 1 % regime 1 (psi=1): n=0.928 c=2.555 k=35.24 % regime 2 (psi=3): n=0.536 c=1.475 k=20.35 The steady state is solved per regime from the derived switching FOCs: higher labour disutility (``psi=3``) yields lower hours and capital, as expected. Output methods -------------- .. list-table:: :header-rows: 1 :widths: 22 78 * - Method - Result * - ``code(p)`` - the derived model as a column cell array of code lines (the canonical artifact) * - ``rs(p,file)`` - write the derived model to a ``.rs`` file * - ``show(p)`` / ``show(p,"agent")`` - print the derived model (optionally one agent's block) * - ``latex(p,file)`` - a LaTeX model appendix (notation + each agent's problem + derived FOCs) API reference ------------- .. list-table:: :header-rows: 1 :widths: 34 66 * - Call - Meaning * - ``p = rise.microfound("Title")`` - create a builder * - ``p.endogenous("x", "y: a description", ...)`` - declare endogenous variables (inline ``"name: description"`` optional) * - ``p.exogenous("eps", ...)`` / ``p.shock(...)`` - declare shocks * - ``p.parameter("beta", ..., switches_on="chain")`` - declare parameters; ``switches_on`` marks a switching parameter * - ``p.chain("name", nstates)`` - declare a Markov chain * - ``p.agent("name", discount="beta")`` - open an agent's problem (returns a fluent handle) * - ``.maximize("U")`` / ``.minimize("L")`` - the period objective * - ``.choose("c","n","k")`` - the choice (control) variables * - ``.subject_to(label="L = R", ...)`` - named equality constraints (multiplier ``_lambda_